#include "Player.h"
#include "GameGUI.h"
#include "GameApp.h"
#include "RewardOrb.h"
#include "Sword.h"

//-------------------------------------------------------------------------------------
/** New player with zero EXP and SkillPoints. */
Player::Player(std::string id) : Actor(id)
{
	mpSkills = boost::shared_ptr<SkillTree>(new SkillTree());
	mpOgreInitData->pos = Ogre::Vector3(0.0f, 5.0f, 0.0f);
	getStats()->setStat(STAT_DAMAGE, 2.0f);
	getStats()->setStat(STAT_RANGE, 2.0f);
	this->readPlayerFromXML();
	this->getSkillTree()->healthTree = 0.0f;
	this->getSkillTree()->energyTree = 0.0f;
	this->getSkillTree()->combatTree = 0.0f;
	this->getSkillTree()->archetype = (Archetype)0;
	mPrepareTime = 1000;
	mLevelPower = 0;
	mSP = 0.0f;
	mEXP = 0.0f;
	mCurrentLevel = 0;

	mNextDirection = 0.0f;
	mPrepareTimeElapsed = 0;
	mTypes.push_front(OBJECT_PLAYER);
	mGunLoaded = false;
	mSwordInHand = false;
	mMoveState = MOVE_IDLE;
	mJumpCounter = 0;
	mLockControls = 0.f;
	mShootPos = 1.0f;
	mPlayAnim = true;
	mPlus = true;
	mpGunParticle = 0;
	mNoBGTime = 0;
	mBlockTime = 0;
	mFlyingTime = 0;
	dontKill = true;
	//gpApp->mpAudio->setBGMusic(SOUND_BG1);
	mpCopyMissile = MissileObjectPtr(new MissileObject("level/missile.xml"));
}
//-------------------------------------------------------------------------------------
/** load saved player */
Player::Player() : Actor("saves/player.xml")
{
	mpSkills = boost::shared_ptr<SkillTree>(new SkillTree());
	readPlayerFromXML();
	mCurrentLevel = 0;

	mNextDirection = 0.0f;
	mPrepareTimeElapsed = 0;
	mTypes.push_front(OBJECT_PLAYER);
	mGunLoaded = false;
	mSwordInHand = false;
	mMoveState = MOVE_IDLE;
	mJumpCounter = 0;
	mLockControls = 0.f;
	mShootPos = 1.0f;
	mPlayAnim = true;
	mPlus = true;
	mpGunParticle = 0;
	mNoBGTime = 0;
	mBlockTime = 0;
	mFlyingTime = 0;
	dontKill = true;
	//gpApp->mpAudio->setBGMusic(SOUND_BG1);
	mpCopyMissile = MissileObjectPtr(new MissileObject("level/missile.xml"));
}
//-------------------------------------------------------------------------------------
Player::~Player()
{
}
//-------------------------------------------------------------------------------------
void Player::readPlayerFromXML()
{
	//load XML file into SettingsManager
	boost::shared_ptr<SettingsManager> sm(new SettingsManager("saves/player.xml"));

	//read Stats
	this->getSkillTree()->healthTree = (float) atof(sm->getSetting("Stats/end").c_str());
	this->getSkillTree()->energyTree = (float) atof(sm->getSetting("Stats/vit").c_str());
	this->getSkillTree()->combatTree = (float) atof(sm->getSetting("Stats/pow").c_str());
	
	// read prepare time for shooting
	mPrepareTime = (unsigned long) atoi(sm->getSetting("Stats/preTime").c_str());

	//set class
	this->getSkillTree()->archetype = (Archetype) atoi(sm->getSetting("Stats/arch").c_str());

	//read progress
	this->mEXP = (float) atof(sm->getSetting("Progess/exp").c_str());
	this->mSP = (float) atof(sm->getSetting("Progress/sp").c_str());
	this->mLevelPower = (char) atoi(sm->getSetting("Progress/power").c_str());
}

void Player::savePlayerToXML()
{
	//load XML file into SettingsManager
	boost::shared_ptr<SettingsManager> sm(new SettingsManager("saves/player.xml"));

	//save position
    ostringstream val;
	btVector3 p = this->getBody()->getWorldTransform().getOrigin();
	val << p.getX();
	sm->setSetting("OgreData/Position/x",val.str());

	val.clear();
	val << p.getY();
	sm->setSetting("OgreData/Position/y",val.str());

	val.clear();
	val << p.getZ();
	sm->setSetting("OgreData/Position/z",val.str());

	//save orientation <- TODO ???

	//save stats
	val.clear();
	val << this->getSkillTree()->healthTree;
	sm->setSetting("Stats/end", val.str());
	val.clear();
	val << this->getSkillTree()->energyTree;
	sm->setSetting("Stats/vit", val.str());
	val.clear();
	val << this->getSkillTree()->combatTree;
	sm->setSetting("Stats/pow", val.str());
	val.clear();
	val << (int) this->getSkillTree()->archetype;
	sm->setSetting("Stats/arch", val.str());
	val.clear();
	val << (int)mPrepareTime;
	sm->setSetting("Stats/preTime", val.str());

	//save progress
	val.clear();
	val << this->mEXP;
	sm->setSetting("Progress/exp", val.str());
	val.clear();
	val << this->mSP;
	sm->setSetting("Progress/sp", val.str());

}
//-------------------------------------------------------------------------------------
void Player::changeClass(Archetype arch)
{
	getSkillTree()->archetype = arch;
	this->mMoveState = MOVE_IDLE;
	//gpApp->mpGameLogic->createParticleSystem(Ogre::String("Funky"),2, cvt(gun->getBody()->getWorldTransform().getOrigin()));
	if(arch == ARCHE_RANGED)
	{
		if(sword) this->removeSword();
		this->equipGun();
	} 
	else if(arch == ARCHE_MELEE)
	{
		if(gun) this->removeGun();
		this->equipSword();
	}
}
//-------------------------------------------------------------------------------------
/** buy a skill in tree. returns flase if you couldn't buy one. */
bool Player::buySkill(Tree tree)
{
	bool success = false;

	if(this->mSP >= 1.0f)
	{
		switch(tree)
		{
		case TREE_HEALTH: if(skillAble(tree))
						  {
							getSkillTree()->healthTree++;  
							getStats()->setStat(STAT_HEALTH, getStats()->getStat(STAT_HEALTH) + healthGain);
							success = true;
						  }
						  break;

		case TREE_ENERGY: if(skillAble(tree))
						  {
							getSkillTree()->energyTree++;  
							getStats()->setStat(STAT_ENERGY, getStats()->getStat(STAT_ENERGY) + energyGain);
							success = true;
						  }
						  break;

		case TREE_COMBAT: if(skillAble(tree))
						  {
							getSkillTree()->combatTree++;  
							getStats()->setStat(STAT_DAMAGE, getStats()->getStat(STAT_DAMAGE) + combatGain);
							success = true;
						  }
						  break;
		default: validateStats();
		}
	}

	if(success) 
	{
		decSkillPoints();
		gpApp->mpAudio->playSound(SOUND_MENU, false, shared_from_this());
	}

	return success;
}
//-------------------------------------------------------------------------------------
/** Returns false, when max score in tree is reached */
bool Player::skillAble(Tree tree)
{
	bool result = false;

	switch(tree)
	{
		case TREE_HEALTH: result = getSkillTree()->healthTree < maxSkillsProBar;
						  break;

		case TREE_ENERGY: result = getSkillTree()->energyTree < maxSkillsProBar;
						  break;

		case TREE_COMBAT: result = getSkillTree()->combatTree < maxSkillsProBar;
						  break;
		default: validateStats();
	}

	return result;
}
//-------------------------------------------------------------------------------------
/* re-calculates the stats from trees (doesn't heal the player).
   note: only resources are validated. maybe need to outsource damage from stats.*/
void Player::validateStats()
{
	// health = initialHealth + (healthGain * heathTreeScore). other stats are the same way.
	this->setResources(RES_HEALTH_CAP, getStats()->getStat(STAT_HEALTH) + healthGain * getSkillTree()->healthTree);
	this->setResources(RES_ENERGY_CAP, getStats()->getStat(STAT_ENERGY) + energyGain * getSkillTree()->energyTree);
	if(this->getSkillTree()->combatTree > 0.f)
	{
		this->getStats()->setStat(STAT_DAMAGE, 2.f + this->getSkillTree()->combatTree * combatGain);
	}
}

boost::shared_ptr<SkillTree> Player::getSkillTree()
{
	return mpSkills;
}
//-------------------------------------------------------------------------------------
/** Use this to increase the players exp. Will grant skills when cap is reached */
void Player::incEXP(float amount)
{
	float newEXP = mEXP + amount;

	if(newEXP >= mMaxEXP)
	{
		incSkillPoints();
		mEXP = newEXP - mMaxEXP;  //save EXP overflow

	} else {
		mEXP = newEXP;
	}
}
//-------------------------------------------------------------------------------------
void Player::decSkillPoints()
{
	//can't have negative SP
	if(mSP >= 0.0f) 
		--mSP;
}
//-------------------------------------------------------------------------------------
void Player::incSkillPoints()
{
	frontout << "gained a skill point." << endl;
	gpApp->mpAudio->playSound(SOUND_LEVELUP, false, shared_from_this());
	//gpApp->mpGameLogic->createParticleSystem(Ogre::String("Funky"),2, cvt(gun->getBody()->getWorldTransform().getOrigin()));
	++mSP;
}
//-------------------------------------------------------------------------------------
const float Player::getEXP(void) const
{
	return mEXP;
}
//-------------------------------------------------------------------------------------
const float Player::getSkillPoints(void) const
{
	return mSP;
}
//-------------------------------------------------------------------------------------
const unsigned int Player::getCurrentLevel(void) const
{
	return mCurrentLevel;
}
//-------------------------------------------------------------------------------------
void Player::setCurrentLevel(const unsigned int level)
{
	mCurrentLevel = level;
}
//-------------------------------------------------------------------------------------
void Player::setSkillPoints(const float sp)
{
	mSP = sp;
}
//-------------------------------------------------------------------------------------
void Player::setEXP(const float exp)
{
	mEXP = exp;
}
//-------------------------------------------------------------------------------------
void Player::collision(BaseObjectPtr obj, btVector3 &point)
{
	switch (obj->getType())
		{
		case OBJECT_REWARDORB:
			{
				gpApp->mpAudio->playSound(SOUND_GAINREWARD, false, shared_from_this());
				orbPtr orb = boost::static_pointer_cast<rewardOrb>(obj);
				collectOrb(orb->getRewardType(), orb->getAmount());
				orb->kill();
			}
			break;
		default: return;
	}
}
//-------------------------------------------------------------------------------------
void Player::collectOrb(RewardType type, float amount)
{
	switch(type)
	{
	case REW_HEALTH: changeResources(RES_HEALTH, amount); break;
	case REW_ENERGY: changeResources(RES_ENERGY, amount); break;
	case REW_EXP: this->incEXP(amount); break;
	case REW_SKILL: this->incSkillPoints(); break;
	default: break;
	}
}

void Player::normalAttack(void)
{
	switch(mMoveState)
	{
	case MOVE_IDLE:
		if(!mStates[MOVE_AIR] && mStates[MOVE_INTERRUPTIBLE])
		{
			mMoveState = MOVE_ATTACK;
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	case MOVE_ATTACK:
		if(!mStates[MOVE_AIR] && mStates[MOVE_INTERRUPTIBLE])
		{
			mMoveState = MOVE_ATTACK2;
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	case MOVE_ATTACK2:
		if(!mStates[MOVE_AIR] && mStates[MOVE_INTERRUPTIBLE])
		{
			mMoveState = MOVE_ATTACK3;
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	default:
		break;
	}
}

void Player::block(void)
{
	if (mMoveState == MOVE_STUN) return;

	switch (mMoveState)
	{
	case MOVE_ATTACK2:
		if(!mStates[MOVE_AIR] && mStates[MOVE_INTERRUPTIBLE])
		{
			mMoveState = MOVE_ATTACK_SHIELD;
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	default:
		if(!mStates[MOVE_AIR] && (mStates[MOVE_INTERRUPTIBLE] || mMoveState == MOVE_IDLE))
		{
			mMoveState = MOVE_BLOCK;
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	}
}

void Player::prepareShoot(void)
{
	if (!mStates[MOVE_AIR] && mStates[MOVE_INTERRUPTIBLE])
	{
		mStates[MOVE_INTERRUPTIBLE] = false;
		mMoveState = MOVE_PREPARESHOOT;

		// Set player direction
		if (getMouseXDirection() > 0.0f) lookLeft();
		else lookRight();

		float angle = getMouseAngle();
		if (angle > 2.65f) angle = 2.65f;
		else if (angle < 0.6) angle = 0.4;
		else angle -= 0.3;
		mShootPos = angle / 3.14f;

		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Shoot"), false, false, 1.0f, 0.0f, 0.1f);

		if(this->hasType(OBJECT_PLAYER)) 
			mpGunParticle = gpApp->mpGameLogic->createParticleSystem(Ogre::String("FillWeapon"), cvt(gun->getBody()->getWorldTransform().getOrigin()));
	}
}

void Player::shoot()
{
	if (mMoveState == MOVE_PREPARESHOOT)
	{
		// Only shoot if players weapon is in position
		//if (!mShootPos)
		{
			const OIS::MouseState &ms = gpApp->mpInput->getMouse()->getMouseState();
			btTransform trans = mpBody->getWorldTransform();
			btVector3 pos = trans.getOrigin();
			Ogre::Matrix3 rotmat;
			
			const Ogre::Matrix4 &nodeTrans = mpEntityNode->_getFullTransform();
			Ogre::Bone *swordBone = getEntity()->getSkeleton()->getBone(Ogre::String("Hand.R"));
			Ogre::Vector3 swordPos = nodeTrans * swordBone->_getDerivedPosition();
			Ogre::Bone *shoulderBone = getEntity()->getSkeleton()->getBone(Ogre::String("Forearm.R"));

			btVector3 xAxis = cvt(swordPos - nodeTrans * shoulderBone->_getDerivedPosition());
			btMatrix3x3 basis = mpBody->getWorldTransform().getBasis();

			mpMissile = MissileObjectPtr(new MissileObject(mpCopyMissile, swordPos.x, swordPos.y, swordPos.z, basis, xAxis));

			float dmg = getStats()->getStat(STAT_DAMAGE);
			if(mGunLoaded)
			{
				if(this->changeResources(RES_ENERGY, -10.f))
				{
					mpMissile->setPower(dmg + 2.f + 2.f * dmg);
					mpMissile->setLoadedParticle(true);
					if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_BIGSHOT, false, shared_from_this());
				}
			}
			else if(this->changeResources(RES_ENERGY, -5.f))
			{
				mpMissile->setPower(dmg - 1.f);
				if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_LASER, false, shared_from_this());
			}
		
			gpApp->mpGameLogic->addObject(mpMissile);
		}
	
		mMoveState = MOVE_IDLE;
		mStates[MOVE_INTERRUPTIBLE] = true;
		mPrepareTimeElapsed = 0;
		mPlus = true;
		mPlayAnim = true;

		gpApp->mpGameLogic->removeParticleSystem(mpGunParticle, 1500);
		mpGunParticle = 0;
		mGunLoaded = false;
	}
}

void Player::equipSword(std::string id)
{
	removeSword();
	strstream name;
	name<<"sword"<<gpApp->mpGameLogic->nameCounter++;
	sword = SwordPtr(new Sword(id));
	sword->getOgreInitData()->name = OStringPtr(new Ogre::String(name.str()));
	strstream name_col;
	name_col<<"sword_col"<<gpApp->mpGameLogic->nameCounter++;
	sword->getBulletInitData()->collisionName = OStringPtr(new Ogre::String(name_col.str()));
	gpApp->mpGameLogic->addObject(sword);
	sword->saveOffset(sword->getBody()->getWorldTransform());
	swordToBack();
	sword->setOwner(boost::shared_static_cast<Actor>(shared_from_this()));
}

void Player::removeSword()
{
	if(sword)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(sword);
		sword->kill();
		sword = SwordPtr();
	}
}

void Player::equipGun(std::string id)
{
	removeGun();
	strstream name;
	name<<"gun"<<gpApp->mpGameLogic->nameCounter++;
	gun = SwordPtr(new Sword(id));
	gun->getOgreInitData()->name = OStringPtr(new Ogre::String(name.str()));
	gpApp->mpGameLogic->addObject(gun);
	gun->saveOffset(gun->getBody()->getWorldTransform());
	gun->setOwner(boost::shared_static_cast<Actor>(shared_from_this()));

	gpApp->mpGameLogic->getUpdatePairs()->removeObject(gun);
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToBone(gun, getEntityNode(), getEntity()->getSkeleton()->getBone(Ogre::String("Forearm.R")), gun->getOffset());
}

void Player::removeGun()
{
	if(gun)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(gun);
		gun->kill();
		gun = SwordPtr();
	}
}

void Player::equipShield(std::string id)
{
	removeShield();
	strstream name;
	name<<"shield"<<gpApp->mpGameLogic->nameCounter++;
	shield = SwordPtr(new Sword(id));
	shield->getOgreInitData()->name = OStringPtr(new Ogre::String(name.str()));
	gpApp->mpGameLogic->addObject(shield);
	shield->saveOffset(shield->getBody()->getWorldTransform());
	shield->setOwner(boost::shared_static_cast<Actor>(shared_from_this()));

	gpApp->mpGameLogic->getUpdatePairs()->removeObject(shield);
	gpApp->mpGameLogic->getUpdatePairs()->attachObjectToBone(shield, getEntityNode(), getEntity()->getSkeleton()->getBone(Ogre::String("Forearm.L")), shield->getOffset());
}

void Player::removeShield()
{
	if(shield)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(shield);
		shield->kill();
		shield = SwordPtr();
	}
}

void Player::update(const unsigned long elapsedTime)
{
	Ogre::AnimationState *anim;

	float elapsedSec = elapsedTime / 1000.f;

	mpAnimator->stepAnimation(elapsedTime);

	updateAirStatus(elapsedTime);
	updateWallStatus(elapsedTime);
	updateStun(elapsedTime);
	updateInvulnerable(elapsedTime);

	// update time player doesnt need energy for blocking
	if (mBlockTime < elapsedTime) mBlockTime = 0;
	else mBlockTime -= elapsedTime;

	// Update the flying time of the player
	mFlyingTime += elapsedTime;
	//if (mFlyingTime > 4000 && !dontKill) changeResources(RES_HEALTH, -100000);

	//updating locked controls
	mLockControls -= elapsedSec;
	if(mLockControls < 0.f) mLockControls = 0.f;

	mStates[MOVE_INTERRUPTIBLE] = true;

	switch(mMoveState)
	{
	case MOVE_ATTACK:
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Attack_01"),false, false, 1.f, 0.f,0.1f);

		//if the animation ended move the sword back to the back and idle
		anim = mpAnimator->getAnimation(Ogre::String("Attack_01"));
		if (anim->getTimePosition() == anim->getLength())
		{
			mMoveState = MOVE_IDLE;
			if(sword)
			{
				sword->deactivate();
				swordToBack();
			}
			break;
		}
		//activating sword only during the swing
		if(anim->getTimePosition() < 0.65f && anim->getTimePosition() > 0.17f)
		{
			if(!sword->isActivated() && sword)
			{
				sword->activate();
				executeCombo(0.f, 0.f, 0.1f, 0.f);
				if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_HUMM, false, shared_from_this());
			}
		} else {
			if(sword->isActivated() && sword)
			{
				sword->deactivate();
			}
		}
		//only move sword to hand when the hand has reached the shoulder
		if(anim->getTimePosition() > 0.17f)
		{
			if(!mSwordInHand)
			{
				swordToHand();
			}
		} else
		{
			if(mSwordInHand)
			{
				swordToBack();
			}
		}
		//player can be interrupted after an attack
		if(anim->getTimePosition() < 0.65f)
		{
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	case MOVE_ATTACK2:
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Attack_02"),false, false, 1.f, 0.f,0.1f);

		//if the animation ended move the sword back to the back and idle
		anim = mpAnimator->getAnimation(Ogre::String("Attack_02"));
		if (anim->getTimePosition() == anim->getLength())
		{
			mMoveState = MOVE_IDLE;
			if(sword)
			{
				sword->deactivate();
				swordToBack();
			}
			break;
		}
		//activating sword only during the swing
		if(anim->getTimePosition() < 0.55f)
		{
			if(!sword->isActivated() && sword)
			{
				sword->activate();
				executeCombo(2.f, 0.2f, 3.f, 5.f);
			}
		} else {
			if(sword->isActivated() && sword)
			{
				sword->deactivate();
			}
		}
		//player can be interrupted after an attack
		if(anim->getTimePosition() < 0.55f)
		{
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	case MOVE_ATTACK3:
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Attack_03"),false, false, 1.f, 0.f,0.1f);

		//if the animation ended move the sword back to the back and idle
		anim = mpAnimator->getAnimation(Ogre::String("Attack_03"));
		if (anim->getTimePosition() == anim->getLength())
		{
			mMoveState = MOVE_IDLE;
			if(sword)
			{
				sword->deactivate();
				swordToBack();
			}
			break;
		}
		//activating sword only during the swing
		if(anim->getTimePosition() < 0.55f)
		{
			if(!sword->isActivated() && sword)
			{
				sword->activate();
				if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_HUMM2, false, shared_from_this());
				executeCombo(8.f, 0.5f, 0.3f, 20.f);
			}
		} else {
			if(sword->isActivated() && sword)
			{
				sword->deactivate();
			}
		}
		//player can be interrupted after an attack
		if(anim->getTimePosition() < 0.55f)
		{
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		break;
	case MOVE_ATTACK_SHIELD:
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Attack_Shield"),false, false, 1.f, 0.f,0.01f);
		//deactivate weaopons
		anim = mpAnimator->getAnimation(Ogre::String("Attack_Shield"));
		if (anim->getTimePosition() == anim->getLength())
		{
			mMoveState = MOVE_IDLE;
			if(sword)
			{
				sword->deactivate();
				shield->deactivate();
				swordToBack();
			}
			break;
		}
		//deactivate sword for the shield bash
		if(anim->getTimePosition() <= 0.7 && anim->getTimePosition() > 0.2)
		{
			if(!shield->isActivated())
			{
				shield->activate();
				if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_HUMM2, false, shared_from_this());
				this->executeCombo(10.f, 2.f, 3.f, 40.f);
				sword->deactivate();
			}
		}else
		{
			if(shield->isActivated())
			{
				shield->deactivate();
			}
		}
		if(anim->getTimePosition() <= 0.7)
		{
			mStates[MOVE_INTERRUPTIBLE] = false;
		}
		if(anim->getTimePosition() <= 0.3 && anim->getTimePosition() >= 0.2)
		{
			if(mStates[MOVE_LEFT])
			{
				mpBody->activate();
				setLocalVelocity(btVector3(-2.f * mfMaxSpeed,0.f,0.f));
			}
			else
			{
				mpBody->activate();
				setLocalVelocity(btVector3(2.f * mfMaxSpeed,0.f,0.f));
			}
		}
		break;
	case MOVE_BLOCK:
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Block"),true, false, 1.f, 0.f, 1.0f);
		mStates[MOVE_INTERRUPTIBLE] = false;
		break;
	case MOVE_IDLE:
		// regenerate 1 energy/sec when idling;
		this->changeResources(RES_ENERGY, 2.f*elapsedSec);

		if(mStates[MOVE_AIR])
		{
			gpApp->mpAudio->stopSound(mMoveSound);
			if (getLocalVelocity().y() < 0.f)
			{
				mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_down"),false, false, 1.f, 0.f, 1.0f);
				//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Jump_Down_Lower"));
			} else
			{
				mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_up"));
				//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Jump_Up_Lower"));
			}
		}
		else if (!mStates[MOVE_NORMAL])
		{
			gpApp->mpAudio->stopSound(mMoveSound);
			mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Idle"), true, false, 1.f, 0.f, 1.0f);
			//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Idle_Lower"), true);
		}
		mStates[MOVE_NORMAL] = false;
		break;
	case MOVE_PREPARESHOOT:
		{
			mStates[MOVE_INTERRUPTIBLE] = false;

			float angle = getMouseAngle();
			if (angle > 2.65f) angle = 2.65f;
			else if (angle < 0.6) angle = 0.4;
			else angle -= 0.3;
			mShootPos = angle / 3.14f;

			anim = mpAnimator->getAnimation(Ogre::String("Shoot"));
			anim->setTimePosition(mShootPos * anim->getLength());

			mpGunParticle->setPosition(cvt(gun->getBody()->getWorldTransform().getOrigin()));

			if(!mGunLoaded) mPrepareTimeElapsed += elapsedTime;

			if (mPrepareTimeElapsed >= mPrepareTime)
			{
				if(!mGunLoaded)
					gpApp->mpAudio->playSound(SOUND_ACTIVATION, false, shared_from_this());

				mGunLoaded = true;
			}
			break;
		}
	case MOVE_DEATH:
		{
			mStates[MOVE_INTERRUPTIBLE] = false;

			anim = mpAnimator->getAnimation(Ogre::String("Dead"));
			if (anim->getTimePosition() == anim->getLength())
			{
				frontout << "GAME OVER" << endl;
				gpApp->changeState(State_GameOver);
			}

			break;
		}
	default:
		break;
	}

	if (!mStates[MOVE_AIR])
	{
		mFlyingTime = 0;
		mJumpCounter = 0;
	}
}

/** set Power of sword. set dmg increase and energy cost. 
	also checks for energy								*/
void Player::executeCombo(float cost, float dmgBoost, float stun,float knockBack)
{	
	if(changeResources(RES_ENERGY, -cost) && sword)
	{
		float damage = this->getStats()->getStat(STAT_DAMAGE);
		float pow = damage + dmgBoost * damage;

		if(mMoveState == MOVE_ATTACK_SHIELD)
		{
			shield->setPower(pow, stun, knockBack);
		}
		else sword->setPower(pow, stun, knockBack);
	}
}

void Player::swordToHand(void)
{
	if(sword)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(sword);
		gpApp->mpGameLogic->getUpdatePairs()->attachObjectToBone(sword, getEntityNode(), getEntity()->getSkeleton()->getBone(Ogre::String("Sword")), sword->getOffset());
		mSwordInHand = true;
	}
}
void Player::swordToBack(void)
{
	if(sword)
	{
		gpApp->mpGameLogic->getUpdatePairs()->removeObject(sword);
		gpApp->mpGameLogic->getUpdatePairs()->attachObjectToBone(sword, getEntityNode(), getEntity()->getSkeleton()->getBone(Ogre::String("Sword_Back")), sword->getOffset());
		mSwordInHand = false;
	}
}
//-------------------------------------------------------------------------------------
void Player::recieveDmg(const btVector3 &pos, const float dmg)
{
	if (mInvulnerable > 0.0f && this->hasType(OBJECT_PLAYER)) return;

	if(mMoveState == MOVE_BLOCK || mMoveState == MOVE_ATTACK_SHIELD)
	{
		if(mStates[MOVE_LEFT] == (getLocalVector(pos).x() < 0.f))
		{
			if (mBlockTime) return;
			// 30% of dmg is removed from energy. makes harder hits cost more energy.
			float reducedDMG = 0.3f * dmg;
			this->changeResources(RES_ENERGY, -reducedDMG);
			mBlockTime = 1000;
			return;
		}
	}

	//recieve full damage if unable to block
	changeResources(RES_HEALTH, -dmg);
	mInvulnerable = 1.f;
}
//-------------------------------------------------------------------------------------
void Player::stun(const float dur)
{
	if(mMoveState == MOVE_BLOCK && mMoveState == MOVE_ATTACK_SHIELD) return;
	if (mStun < dur)
	{
		mStun = dur;
		mMoveState = MOVE_STUN;
		mCurrentAttack = MOVE_IDLE;
		mStates[MOVE_ATTACK] = false;
	}
}
//-------------------------------------------------------------------------------------
void Player::knockBack(btVector3 &dir)
{
	if(mMoveState == MOVE_BLOCK && mMoveState == MOVE_ATTACK_SHIELD) return; 
	mpBody->setLinearVelocity(dir);
}
//------------------------------------------------------------------------------------
void Player::moveLeft(void)
{
	if(!mStates[MOVE_INTERRUPTIBLE] || (mLockControls > 0.f)) return;
	mMoveState = MOVE_IDLE;

	if (!mStates[MOVE_LEFT])
	{
		lookLeft();
	}
	
	float maxSpeed;
	if(mStates[MOVE_AIR]) maxSpeed = mfMaxSpeed * 0.5f;
	else maxSpeed = mfMaxSpeed;
	btVector3 vel = getLocalVelocity();
	if(vel.x() > -maxSpeed) vel.setX(-maxSpeed);
	setLocalVelocity(vel);
	
	mpBody->activate();
	
	if(!mStates[MOVE_AIR]) 
	{
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Run"),true);
		//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Run_Lower"),true);
		gpApp->mpAudio->playSound(getSound(SOUND_MOVE));
	}

	mStates[MOVE_NORMAL] = true;
}
//------------------------------------------------------------------------------------
void Player::moveRight(void)
{
	if(!mStates[MOVE_INTERRUPTIBLE] || (mLockControls > 0.f)) return;
	mMoveState = MOVE_IDLE;

	if (mStates[MOVE_LEFT])
	{
		lookRight();
	}
	
	float maxSpeed;
	if(mStates[MOVE_AIR]) maxSpeed = mfMaxSpeed * 0.5f;
	else maxSpeed = mfMaxSpeed;
	btVector3 vel = getLocalVelocity();
	if(vel.x() < maxSpeed) vel.setX(maxSpeed);
	setLocalVelocity(vel);

	mpBody->activate();
	
	if(!mStates[MOVE_AIR]) 
	{
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Run"),true);
		//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Run_Lower"),true);
		gpApp->mpAudio->playSound(getSound(SOUND_MOVE));
	}

	mStates[MOVE_NORMAL] = true;
}
//------------------------------------------------------------------------------------
void Player::jump(void)
{
	if(!mStates[MOVE_INTERRUPTIBLE] || (mJumpCounter > 1)) return;
	mMoveState = MOVE_IDLE;
	btVector3 vel = getLocalVelocity();
	if(vel.y() < mfJumpPower) vel.setY(mfJumpPower);
	++mJumpCounter;

	//walljump
	if(mStates[MOVE_AIR])
	{
		mFlyingTime = 0;
		if(mStates[MOVE_WALL_RIGHT])
		{
			//walljump only activates if actively moving in the direction the wall and enough energy
			if(this->changeResources(RES_ENERGY, -5.f))
			{
				vel.setX(mfJumpPower * -0.5f);
				mJumpCounter = 1;
				mLockControls = 0.3f;
				lookLeft();
			}
		}else if(mStates[MOVE_WALL_LEFT])
		{
			//walljump only activates if actively moving in the direction the wall and enough energy
			if(this->changeResources(RES_ENERGY, -5.f))
			{
				vel.setX(mfJumpPower * 0.5f);
				mJumpCounter = 1;
				mLockControls = 0.3f;
				lookRight();
			}
		}
	}
	//forcing the air status, so the counter isnt instantly reset 
	//and the friction doesnt kick in
	mForceAir = 0.1f;
	//remove friction so you dont get stuck on walls
	if(mpBody->getFriction() != 0.f)
	{
		savedFriction = mpBody->getFriction();
		mpBody->setFriction(0.f);
	}

	//only do double jump when there is enough energy.
	if(mJumpCounter > 1 )
	{

		if (this->changeResources(RES_ENERGY, -10.0f)) {}
		else return;
	} 

	setLocalVelocity(vel);
	mpBody->activate();
	mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_up"));
	gpApp->mpAudio->stopSound(getSound(SOUND_MOVE));
	if(this->hasType(OBJECT_PLAYER)) gpApp->mpAudio->playSound(SOUND_JUMP, false, shared_from_this());
}

void Player::unblock(void)
{
	if(mMoveState == MOVE_BLOCK) mMoveState = MOVE_IDLE;
}

const char Player::getLevelPower() const
{
	return mLevelPower;
}

void Player::setLevelPower(const char power)
{
	mLevelPower = power;
}